/*************************************************************
*
* M3D_BOX.CPP
*
* Box functionality
*************************************************************/
#include <stdafx.h>
#include <ds\ds_string.h>
#include <ds\ds_algorithm.h>

//#include <ap_comm.h>
//#include <m3d.h>

//
// for export
//
m3dV  m3dBoxNorm[6] =
{ { 0.f,  0.f, -1.f},
  { 1.f,  0.f,  0.f},
  { 0.f,  0.f,  1.f},
  {-1.f,  0.f,  0.f},
  { 0.f, -1.f,  0.f},
  { 0.f,  1.f,  0.f},
};


/*************************************************************
* m3dBOX::Expand()
*
* Expand box by a given value
*************************************************************/
void m3dBOX::Expand (float value)
{
   bll.x -= value;
   bll.y -= value;
   bll.z -= value;
   fur.x += value;
   fur.y += value;
   fur.z += value;
   return;
}
/*************************************************************
* m3dBOX::Check()
*
* Check the validity of box
*************************************************************/
BOOL m3dBOX::Check () const
{
   if (bll.x < fur.x + M3D_EPSILON ||
      bll.y < fur.y + M3D_EPSILON ||
      bll.z < fur.z + M3D_EPSILON) {
         return TRUE;
   }
   return FALSE;
}
/*************************************************************
* m3dBOX::IsBelongPoint()
*
* IN:  p     - test point
* RET: TRUE  - p belongs to the box
*      FALSE - otherwise
*
* Check if a point belongs to box
*************************************************************/
BOOL m3dBOX::IsBelongPoint (const m3dV *p) const
{
   // check the validity of the box
   ASSERT(Check());

   if (p->x > bll.x - 100.f*M3D_EPSILON &&
      p->y > bll.y - 100.f*M3D_EPSILON &&
      p->z > bll.z - 100.f*M3D_EPSILON &&
      p->x < fur.x + 100.f*M3D_EPSILON &&
      p->y < fur.y + 100.f*M3D_EPSILON &&
      p->z < fur.z + 100.f*M3D_EPSILON )  {
         return TRUE;
   }

   return FALSE;
}
/*************************************************************
* m3dBOX::DistPoint()
* 
* Calculate distance between point and bounding box
*
* IN:  p        - point
* OUT: vClosest - nearest point on bounding box (may be NULL)
*      dist     - distance to nearest point (may be NULL)
*      faceNmb  - face index (may be NULL)
* RET: M3D_DIST_FOUND/M3D_DIST_NOTFOUND/M3D_DIST_BELONG
*
* NOTE: if 'p' belongs to the box, vClosest/dist are not filled
*************************************************************/
int m3dBOX::DistPoint (const m3dV *p, float distMax, m3dV *vClosest, float *dist, int *faceNmb)
{
   int   typeDist;
   float distMin;
   m3dV  vClos;
   int   face = 0;

   if (faceNmb) {
      *faceNmb = 0;
   }

   if (IsBelongPoint(p)) {
      if (vClosest) {
         *vClosest = *p;
      }
      if (dist) {
         *dist = 0.f;
      }
      return M3D_DIST_BELONG;
   }

   // find XZ-distMin
   if (p->x > fur.x) {
      if (p->z > fur.z) {
         distMin = m3xzDist(p, &fur);
         vClos = fur;
      } else if (p->z < bll.z) {
         vClos.x = fur.x;
         vClos.z = bll.z;
         distMin = m3xzDist(p, &vClos);
      } else {
         distMin = p->x - fur.x;
         vClos.x = fur.x;
         vClos.z = p->z;
      }
      face = 1;
   } else if (p->x < bll.x) {
      if (p->z < bll.z) {
         distMin = m3xzDist(p, &bll);
         vClos = bll;
      } else if (p->z > fur.z) {
         vClos.x = bll.x;
         vClos.z = fur.z;
         distMin = m3xzDist(p, &vClos);
      } else {
         distMin = bll.x - p->x;
         vClos.x = bll.x;
         vClos.z = p->z;
      }
      face = 3;
   } else {
      if (p->z > fur.z) {
         distMin = p->z - fur.z;
         vClos.x = p->x;
         vClos.z = fur.z;
         face = 2;
      } else if (p->z < bll.z) {
         distMin = bll.z - p->z;
         vClos.x = p->x;
         vClos.z = bll.z;
         face = 0;
      } else {
         vClos.x = p->x;
         vClos.z = p->z;
         distMin = 0.f;
      }
   }

   if (p->y < bll.y) {
      if (bll.y - p->y > distMax) {
         return M3D_DIST_NOTFOUND;
      }
      typeDist = 0;
      face     = 4;
      vClos.y  = bll.y;
   } else if (p->y > fur.y) {
      if (p->y - fur.y > distMax) {
         return M3D_DIST_NOTFOUND;
      }
      typeDist = 1;
      face     = 5;
      vClos.y  = fur.y;
   } else {
      // face - already setup
      typeDist = 2;
      vClos.y  = p->y;
   }

   if (typeDist == 0) {
      distMin = m3dPow2(distMin) + m3dPow2(bll.y - p->y);
   } else if (typeDist == 1) {
      distMin = m3dPow2(distMin) + m3dPow2(fur.y - p->y);
   } else {
      if (distMin > distMax) {
         return M3D_DIST_NOTFOUND;
      }
      if (dist) {
         *dist = distMin;
      }
      goto _exit_found;
   }

   if (distMin > distMax * distMax) {
      return M3D_DIST_NOTFOUND;
   }
   if (dist) {
      *dist = m3dSqrt(distMin);
   }

_exit_found:
   if (vClosest) {
      *vClosest = vClos;
   }
   if (faceNmb) {
      *faceNmb = face;
   }
   return M3D_DIST_FOUND;
}
/*************************************************************
* m3dBOX::DistPoint_2()
* 
* Calculate square of the distance between point and bounding box
*
* IN:  p        - point
*      distMax  - square of the distance bound
* OUT: vClosest - nearest point on bounding box (may be NULL)
*      dist     - square of the distance to nearest point (may be NULL)
*      faceNmb  - face index (may be NULL)
* RET: M3D_DIST_FOUND/M3D_DIST_NOTFOUND/M3D_DIST_BELONG
*
* NOTE: if 'p' belongs to the box, vClosest/dist are not filled
*************************************************************/
int m3dBOX::DistPoint_2 (m3dV *p, float distMax_2, m3dV *vClosest, float *dist_2, int *faceNmb)
{
   int   typeDist;
   float distMin_2;
   m3dV  vClos;
   int   face = 0;

   if (faceNmb) {
      *faceNmb = 0;
   }

   if (IsBelongPoint(p)) {
      if (vClosest) {
         *vClosest = *p;
      }
      if (dist_2) {
         *dist_2 = 0.f;
      }
      return M3D_DIST_BELONG;
   }

   // find XZ-distMin
   if (p->x > fur.x) {
      if (p->z > fur.z) {
         distMin_2 = m3xzDist_2(p, &fur);
         vClos = fur;
      } else if (p->z < bll.z) {
         vClos.x = fur.x;
         vClos.z = bll.z;
         distMin_2 = m3xzDist_2(p, &vClos);
      } else {
         distMin_2 = m3dPow2(p->x - fur.x);
         vClos.x = fur.x;
         vClos.z = p->z;
      }
      face = 1;
   } else if (p->x < bll.x) {
      if (p->z < bll.z) {
         distMin_2 = m3xzDist_2(p, &bll);
         vClos = bll;
      } else if (p->z > fur.z) {
         vClos.x = bll.x;
         vClos.z = fur.z;
         distMin_2 = m3xzDist_2(p, &vClos);
      } else {
         distMin_2 = m3dPow2(bll.x - p->x);
         vClos.x = bll.x;
         vClos.z = p->z;
      }
      face = 3;
   } else {
      if (p->z > fur.z) {
         distMin_2 = m3dPow2(p->z - fur.z);
         vClos.x = p->x;
         vClos.z = fur.z;
         face = 2;
      } else if (p->z < bll.z) {
         distMin_2 = m3dPow2(bll.z - p->z);
         vClos.x = p->x;
         vClos.z = bll.z;
         face = 0;
      } else {
         vClos.x = p->x;
         vClos.z = p->z;
         distMin_2 = 0.f;
      }
   }

   if (p->y < bll.y) {
      if (m3dPow2(bll.y - p->y) > distMax_2) {
         return M3D_DIST_NOTFOUND;
      }
      typeDist = 0;
      face     = 4;
      vClos.y  = bll.y;
   } else if (p->y > fur.y) {
      if (m3dPow2(p->y - fur.y) > distMax_2) {
         return M3D_DIST_NOTFOUND;
      }
      typeDist = 1;
      face     = 5;
      vClos.y  = fur.y;
   } else {
      // face - already setup
      typeDist = 2;
      vClos.y  = p->y;
   }

   if (typeDist == 0) {
      distMin_2 = distMin_2 + m3dPow2(bll.y - p->y);
   } else if (typeDist == 1) {
      distMin_2 = distMin_2 + m3dPow2(fur.y - p->y);
   }
   if (distMin_2 > distMax_2) {
      return M3D_DIST_NOTFOUND;
   }
   if (dist_2) {
      *dist_2 = distMin_2;
   }
   if (vClosest) {
      *vClosest = vClos;
   }
   if (faceNmb) {
      *faceNmb = face;
   }
   return M3D_DIST_FOUND;
}

/*************************************************************
* m3dBOX::IsIsectBox()
*
* Check intersecton of box with box
* IN:   pBB - box
* RET:  true/false
*************************************************************/
bool m3dBOX::IsIsectBox(const m3dBOX *pBB) const
{
   // WE WANT TO CHECK:
   //for (int  i = 0; i < 3; i ++) {
   //   if (m3dVectComponent(&fur, i) < m3dVectComponent(&pBB->bll, i) ||
   //      m3dVectComponent(&bll, i) > m3dVectComponent(&pBB->fur, i)) {
   //         return false;
   //   }
   //}
   //return true;

   // instead of the above we avoid such small cycles
   #define OUT_BY_IDX(i) m3dVectComponent(&fur, i) < m3dVectComponent(&pBB->bll, i) \
                      || m3dVectComponent(&bll, i) > m3dVectComponent(&pBB->fur, i)
   if (OUT_BY_IDX(0) || OUT_BY_IDX(1) || OUT_BY_IDX(2)) {
      return false;
   }
   return true;
}

static int _m3dGetPointCodeBox(m3dV *point, float x1, float y1, float z1, float x2, float y2, float z2);

/*************************************************************
'* m3dBOX::m3dBOX()
'* 
'* IN : obb
'*************************************************************/
m3dBOX::m3dBOX(m3dOBB* obb)
{
   m3dV vList[8];

   obb->GetVertices(vList);
   Calc(vList, 8);
}

/*************************************************************
* m3dBOX::IsBelongPointXZ()
*
* IN:  p     - test point
* RET: TRUE  - p belongs to the box
*      FALSE - otherwise
*
* Check if a point belongs to box
*************************************************************/
BOOL m3dBOX::IsBelongPointXZ (m3dV *p)
{
   // check the validity of the box
   ASSERT(Check());

   if (p->x > bll.x - 100.f*M3D_EPSILON &&
       p->z > bll.z - 100.f*M3D_EPSILON &&
       p->x < fur.x + 100.f*M3D_EPSILON &&
       p->z < fur.z + 100.f*M3D_EPSILON )  {
      return TRUE;
   }
   return FALSE;
}
/*************************************************************
* m3dBOX::IsectCyl ()
*    Test isection of cylynder and box
*************************************************************/
BOOL m3dBOX::IsectCyl (m3dV *vFrom, m3dV *dir, float distMax, float radius, m3dV *vClosest, float *dist, int *faceNmb)
{
   int rc;

   m3dBOX::Expand(radius);
   rc = m3dBOX::IsectRay(vFrom, dir, distMax, vClosest, dist, faceNmb);
   m3dBOX::Expand(-radius);

   return rc != M3D_ISECT_NO;
}

/*************************************************************
* m3dBOX::IsectRay()
*
* Intersect a ray with the EXTERNAL surface of box
* NOTE: If point belongs to box, the intersection data (v, dist)
*       is not computed
*
* IN:  rayOrg/rayDir - ray origin/direction
*      distMax       - distance for alpha-cut
* OUT: v             - point if intersection (if exists)
*      dist          - distance between rayOrg and v
* RET: M3D_ISECT_NO/M3D_ISECT_YES/M3D_ISECT_BELONG
*************************************************************/
int m3dBOX::IsectRay (m3dV *rayOrg, m3dV *rayDir, float distMax, m3dV *v, float *dist, int *faceNmb)
{
   float distMax_2;
   int   indFace;
   m3dV  *vNorm, point, vert;

   // check the validity of the box
   ASSERT(Check());

   if (IsBelongPoint(rayOrg))  {
      // fill out
      if (v)  {
         *v = *rayOrg;
      }
      if (dist) {
         *dist = 0;
      }
      if (faceNmb) {
         *faceNmb = 0;
      }
      // as ray origin belongs to box then ray intersects box
      return M3D_ISECT_BELONG;
   }

   distMax_2 = m3dPow2(distMax);
   for (indFace = 0; indFace < 6; indFace++ )  {
      vNorm = &m3dBoxNorm[indFace];

      // backface test
      if (m3dDotProduct(vNorm, rayDir) > -M3D_EPSILON)   {
         continue;
      }

      GetFacePoint(indFace, &point);
      if (m3dIsectRayVNPlane(rayOrg, rayDir, &point, vNorm, &vert)) {
         // suppose, that box is the convex obj and, so, if intersection between
         // any of the plane of box and ray happens and it is farther, than dist,
         // then we do not need to intersect it with other planes of box
         if (distMax_2 < m3dDist_2(&vert, rayOrg))   {
            return M3D_ISECT_NO;
         }

         // ray intersects with the plane of box face polygons;
         // check if it intersects directly with the polygon
         if (IsBelongPointFace(indFace, &vert)) {
            if (v)  {
               *v = vert;
            }
            if (dist) {
               *dist = m3dDist(&vert,rayOrg);
            }
            if (faceNmb) {
               *faceNmb = indFace;
            }
            return M3D_ISECT_YES;
         }
      }
   }
   return M3D_ISECT_NO;
}





/*************************************************************
* m3dBOX::GetDistToPoint()
* 
* Calculate nearest distance square between point and bounding box
*************************************************************/
float m3dBOX::GetDistToPointSq(const m3dV & point) const
{
   m3dV delta;
   delta.x = 0.0f;
   delta.y = 0.0f;
   delta.z = 0.0f;

   if (point.x < bll.x)
      delta.x = bll.x - point.x;
   else if (point.x > fur.x)
      delta.x = point.x - fur.x;

   if (point.y < bll.y)
      delta.y = bll.y - point.y;
   else if (point.y > fur.y)
      delta.y = point.y - fur.y;

   if (point.z < bll.z)
      delta.z = bll.z - point.z;
   else if (point.z > fur.z)
      delta.z = point.z - fur.z;

   return delta.x*delta.x + delta.y*delta.y + delta.z*delta.z;
}


/**************************************************************************
* m3dBOX::Translate ()
*
**************************************************************************/
void m3dBOX::Translate (m3dMATR &matr)
{
   matr.TransformPoint(&bll);
   matr.TransformPoint(&fur);
   return;
}


/*******************************************************************
* m3dBOX::CalcStart()
*
* Create a default box for expansion by adding points
*******************************************************************/
void m3dBOX::CalcStart (void)
{
   m3dMakeVector(&bll,  M3D_INFINITY,  M3D_INFINITY,  M3D_INFINITY);
   m3dMakeVector(&fur, -M3D_INFINITY, -M3D_INFINITY, -M3D_INFINITY);
   return;
}

/*******************************************************************
* m3dBOX::CalcUpdate()
*
* Expand box accounting for a new point
*******************************************************************/
void m3dBOX::CalcUpdate (const m3dV * __RESTRICT v)
{
   m3dCheckValid(v);

	bll.x = m3dFMin(bll.x, v->x);
	bll.y = m3dFMin(bll.y, v->y);
	bll.z = m3dFMin(bll.z, v->z);
	fur.x = m3dFMax(fur.x, v->x);
	fur.y = m3dFMax(fur.y, v->y);
	fur.z = m3dFMax(fur.z, v->z);
   return;
}

/*******************************************************************
* m3dBOX::CalcUpdate()
*
*******************************************************************/
void m3dBOX::CalcUpdate (const m3dV *v, float radius)
{
   m3dBOX boxUpdate;
   boxUpdate.bll = boxUpdate.fur = *v;
   boxUpdate.bll.x -= radius;
   boxUpdate.bll.y -= radius;
   boxUpdate.bll.z -= radius;
   boxUpdate.fur.x += radius;
   boxUpdate.fur.y += radius;
   boxUpdate.fur.z += radius;
   CalcUpdate(&boxUpdate);
   return;
}

/*******************************************************************
* m3dBOX::CalcUpdate()
*
* Expand box accounting for a new points
*******************************************************************/
void m3dBOX::CalcUpdate (const m3dV *pointList, int nPoint)
{
   int  i;

   ASSERT(nPoint > 0);

   for (i = 0; i < nPoint; i ++) {
      CalcUpdate(&pointList[i]);
   }
   return;
}
/*******************************************************************
* m3dBOX::CalcUpdate()
*
* Expand box accounting for a new BOX
*******************************************************************/
void m3dBOX::CalcUpdate (const m3dBOX *pB)
{
   CalcUpdate(&pB->bll);
   CalcUpdate(&pB->fur);
   return;
}
/*******************************************************************
* m3dBOX::CalcUpdateXZ ()
*
* Expand box accounting for a new point
*******************************************************************/
void m3dBOX::CalcUpdateXZ (m2dV *v)
{
   if (bll.x > v->x) {
      bll.x = v->x;
   }
   if (bll.z > v->y) {
      bll.z = v->y;
   }
   if (fur.x < v->x) {
      fur.x = v->x;
   }
   if (fur.z < v->y) {
      fur.z = v->y;
   }
   return;
}
/*******************************************************************
* m3dBOX::CalcUpdateXZ ()
*
* Expand box accounting for a new points
*******************************************************************/
void m3dBOX::CalcUpdateXZ (m2dV *pointList, int nPoint)
{
   int  i;

   ASSERT(nPoint > 0);

   for (i = 0; i < nPoint; i ++) {
      CalcUpdateXZ(&pointList[i]);
   }
   return;
}
/*************************************************************
* m3dBOX::Calc()
*
* IN:  pointList  - a list of points
*      nPoint     - bnumber of points in the list
*      sideMinLen - minimal length of a box side
* OUT: this       - computed box
*
* Calculate box of the point set and apply default box
* expansion
*************************************************************/
void m3dBOX::Calc (m3dV * __RESTRICT pointList, int nPoint, float sideMinLen)
{
   int   i;

   ASSERT(nPoint > 0);

   fur = bll = *pointList++;

   for (i = 1; i < nPoint; i++)  {
      STRONG_ASSERT1(m3dCheckFinite(pointList->x), *pointList);
      STRONG_ASSERT1(m3dCheckFinite(pointList->y), *pointList);
      STRONG_ASSERT1(m3dCheckFinite(pointList->z), *pointList);

      m3dUpdateMax(fur.x, pointList->x);
      m3dUpdateMax(fur.y, pointList->y);
      m3dUpdateMax(fur.z, pointList->z);

      m3dUpdateMin(bll.x, pointList->x);
      m3dUpdateMin(bll.y, pointList->y);
      m3dUpdateMin(bll.z, pointList->z);

      pointList++;
   }

   if (m3dFAbs(fur.x - bll.x) < sideMinLen) {
      fur.x  = bll.x + sideMinLen;
   }
   if (m3dFAbs(fur.y - bll.y) < sideMinLen) {
      fur.y  = bll.y + sideMinLen;
   }
   if (m3dFAbs(fur.z - bll.z) < sideMinLen) {
      fur.z  = bll.z + sideMinLen;
   }
   return;
}

/*************************************************************
* m3dBOX::CalcXZ()
*
* IN:  nPoint     - the number of points in the list
*      pointList  - a list of points
*      sideMinLen - minimal length of a box side
* OUT: this       - computed box (XZ fields of BLL and fur are used)
*
* Calculate box of the point set and apply default box
* expansion
*************************************************************/
void m3dBOX::CalcXZ (m3dV *pointList, int nPoint, float sideMinLen)
{
   int   i;

   ASSERT(nPoint > 0);

   fur.x = bll.x = pointList->x;
   fur.z = bll.z = pointList->z;
   pointList++;

   for (i = 1; i < nPoint; i++ )  {
      if (fur.x < pointList->x)  {
         fur.x = pointList->x;
      }
      if (fur.z < pointList->z)  {
         fur.z = pointList->z;
      }
      if (bll.x > pointList->x)  {
         bll.x = pointList->x;
      }
      if (bll.z > pointList->z)  {
         bll.z = pointList->z;
      }
      pointList++;
   }
   if (m3dFAbs(fur.x - bll.x) < sideMinLen) {
      fur.x  = bll.x + sideMinLen;
   }
   if (m3dFAbs(fur.z - bll.z) < sideMinLen) {
      fur.z  = bll.z + sideMinLen;
   }
   return;
}
/*************************************************************
* m3dBOX::CalcXZ()
*
* IN:  nPoint     - the number of points in the list
*      pointList  - a list of points
*      sideMinLen - minimal length of a box side
* OUT: this       - computed box (XZ fields of BLL and fur are used)
*
* Calculate box of the point set and apply default box
* expansion
*************************************************************/
void m3dBOX::CalcXZ (m2dV *pointList, int nPoint, float sideMinLen)
{
   int   i;

   ASSERT(nPoint > 0);

   fur.x = bll.x = pointList->x;
   fur.z = bll.z = pointList->y;
   pointList++;

   for (i = 1; i < nPoint; i++ )  {
      if (fur.x < pointList->x)  {
         fur.x = pointList->x;
      }
      if (fur.z < pointList->y)  {
         fur.z = pointList->y;
      }
      if (bll.x > pointList->x)  {
         bll.x = pointList->x;
      }
      if (bll.z > pointList->y)  {
         bll.z = pointList->y;
      }
      pointList++;
   }
   if (m3dFAbs(fur.x - bll.x) < sideMinLen) {
      fur.x  = bll.x + sideMinLen;
   }
   if (m3dFAbs(fur.z - bll.z) < sideMinLen) {
      fur.z  = bll.z + sideMinLen;
   }
   return;
}

/*****************************************************
* m3dBOX::MakePointPoint()
*
* Make box by two points
****************************************************/
void m3dBOX::MakePointPoint (m3dV *p1, m3dV *p2)
{
   ASSERT(p1 && p2);
   m3dV   _bll, _fur;

   _bll.x = min(p1->x, p2->x);
   _bll.y = min(p1->y, p2->y);
   _bll.z = min(p1->z, p2->z);

   _fur.x = max(p1->x, p2->x);
   _fur.y = max(p1->y, p2->y);
   _fur.z = max(p1->z, p2->z);


   bll = _bll;
   fur = _fur;
   return;
}

/*************************************************************
* m3dBOX::GetVertices()
*
* OUT : v   - pointer to the vertex list (at least 8 vertices)
* NOTE: the order of vertices cannot be modified (it's assumed
*       in other functions)
*
* Get box vertices
*************************************************************/
void m3dBOX::GetVertices (m3dV vList_[8]) const
{
   // check the validity of the box
   ASSERT(Check());

   m3dV* __RESTRICT vList = vList_;

   vList[0].x = bll.x;
   vList[0].y = bll.y;
   vList[0].z = bll.z;
   vList[1].x = fur.x;
   vList[1].y = bll.y;
   vList[1].z = bll.z;
   vList[2].x = fur.x;
   vList[2].y = fur.y;
   vList[2].z = bll.z;
   vList[3].x = bll.x;
   vList[3].y = fur.y;
   vList[3].z = bll.z;

   vList[4].x = bll.x;
   vList[4].y = bll.y;
   vList[4].z = fur.z;
   vList[5].x = fur.x;
   vList[5].y = bll.y;
   vList[5].z = fur.z;
   vList[6].x = fur.x;
   vList[6].y = fur.y;
   vList[6].z = fur.z;
   vList[7].x = bll.x;
   vList[7].y = fur.y;
   vList[7].z = fur.z;
   return;
}

/*************************************************************
* m3dBOX::GetFacePoint()
*
* IN:  pBBWCS  - some box in right-hand system (WCS)
*      faceNmb - number of box's face
* OUT: point   - a point from the box's face
*
* Get a point from the external face of the box
*************************************************************/
void m3dBOX::GetFacePoint (int faceNmb, m3dV *point)
{
   switch (faceNmb) {
      case M3D_VOL_FACE_LEFT:  case M3D_VOL_FACE_BACK:  case M3D_VOL_FACE_BOTTOM:
         *point = bll;     // 0
         break;
      case M3D_VOL_FACE_FRONT: case M3D_VOL_FACE_RIGHT: case M3D_VOL_FACE_TOP:
         *point = fur;     // 6
         break;
      default:
         ASSERT(FALSE);
         break;
   }

   return;
}

/*************************************************************
* m3dBOX::GetEdgePoints ()
*
* IN:  edgeNmb  - number of box's edge
* OUT: point1,2 - a point from the box's edge
*
* Get points of the box edge
*************************************************************/
void m3dBOX::GetEdgePoints (int edgeNmb, m3dV *point1, m3dV *point2)
{
   switch (edgeNmb) {
      case 0:
         point1->x = bll.x;
         point1->y = bll.y;
         point1->z = bll.z;
         point2->x = fur.x;
         point2->y = bll.y;
         point2->z = bll.z;
         break;
      case 1:
         point1->x = fur.x;
         point1->y = bll.y;
         point1->z = bll.z;
         point2->x = fur.x;
         point2->y = fur.y;
         point2->z = bll.z;
         break;
      case 2:
         point1->x = fur.x;
         point1->y = fur.y;
         point1->z = bll.z;
         point2->x = bll.x;
         point2->y = fur.y;
         point2->z = bll.z;
         break;
      case 3:
         point1->x = bll.x;
         point1->y = fur.y;
         point1->z = bll.z;
         point2->x = bll.x;
         point2->y = bll.y;
         point2->z = bll.z;
         break;
      case 4:
         point1->x = bll.x;
         point1->y = bll.y;
         point1->z = fur.z;
         point2->x = fur.x;
         point2->y = bll.y;
         point2->z = fur.z;
         break;
      case 5:
         point1->x = fur.x;
         point1->y = bll.y;
         point1->z = fur.z;
         point2->x = fur.x;
         point2->y = fur.y;
         point2->z = fur.z;
         break;
      case 6:
         point1->x = fur.x;
         point1->y = fur.y;
         point1->z = fur.z;
         point2->x = bll.x;
         point2->y = fur.y;
         point2->z = fur.z;
         break;
      case 7:
         point1->x = bll.x;
         point1->y = fur.y;
         point1->z = fur.z;
         point2->x = bll.x;
         point2->y = bll.y;
         point2->z = fur.z;
         break;
      case 8:
         point1->x = bll.x;
         point1->y = bll.y;
         point1->z = bll.z;
         point2->x = bll.x;
         point2->y = bll.y;
         point2->z = fur.z;
         break;
      case 9:
         point1->x = fur.x;
         point1->y = bll.y;
         point1->z = bll.z;
         point2->x = fur.x;
         point2->y = bll.y;
         point2->z = fur.z;
         break;
      case 10:
         point1->x = fur.x;
         point1->y = fur.y;
         point1->z = bll.z;
         point2->x = fur.x;
         point2->y = fur.y;
         point2->z = fur.z;
         break;
      case 11:
         point1->x = bll.x;
         point1->y = fur.y;
         point1->z = bll.z;
         point2->x = bll.x;
         point2->y = fur.y;
         point2->z = fur.z;
      break;
      default:
         ASSERT(FALSE);
         break;
   }
   return;
}
/*************************************************************
* m3dBOX::GetEdgePoints ()
*
* IN:  edgeNmb  - number of box's edge
* OUT: point1,2 - a point from the box's edge
*
* Get points of the box edge
*************************************************************/
void m3dBOX::GetEdgePointIndices (int edgeNmb, int *ind1, int *ind2)
{
   switch (edgeNmb) {
      case 0:
         *ind1 = 0;
         *ind2 = 1;
         break;
      case 1:
         *ind1 = 1;
         *ind2 = 2;
         break;
      case 2:
         *ind1 = 2;
         *ind2 = 3;
         break;
      case 3:
         *ind1 = 3;
         *ind2 = 0;
         break;
      case 4:
         *ind1 = 4;
         *ind2 = 5;
         break;
      case 5:
         *ind1 = 5;
         *ind2 = 6;
         break;
      case 6:
         *ind1 = 6;
         *ind2 = 7;
         break;
      case 7:
         *ind1 = 7;
         *ind2 = 4;
         break;
      case 8:
         *ind1 = 0;
         *ind2 = 4;
         break;
      case 9:
         *ind1 = 1;
         *ind2 = 5;
         break;
      case 10:
         *ind1 = 2;
         *ind2 = 6;
         break;
      case 11:
         *ind1 = 3;
         *ind2 = 7;
      break;
      default:
         ASSERT(FALSE);
         break;
   }
   return;
}

/*************************************************************
* m3dBOX::GetFace()
*
* IN:  faceNmb - number of box's face
* OUT: poly    - box face
*
* Get a face from the box external surface
*************************************************************/
void m3dBOX::GetFace (int faceNmb, m3dPOLY *poly, m3dV *norm)
{
   if (faceNmb < 0 || faceNmb >= 6) {
      ASSERT(FALSE);
      faceNmb = 0;
   }
   if (norm) {
      *norm = m3dBoxNorm[faceNmb];
   }

   if (poly) {
      switch (faceNmb) {
         case M3D_VOL_FACE_LEFT:
            *m3dPolyVertex(poly,0) = bll;     // 0
            m3dPolyVertexX(poly,1) = bll.x;   // 3
            m3dPolyVertexY(poly,1) = fur.y;
            m3dPolyVertexZ(poly,1) = bll.z;
            m3dPolyVertexX(poly,2) = fur.x;   // 2
            m3dPolyVertexY(poly,2) = fur.y;
            m3dPolyVertexZ(poly,2) = bll.z;
            m3dPolyVertexX(poly,3) = fur.x;   // 1
            m3dPolyVertexY(poly,3) = bll.y;
            m3dPolyVertexZ(poly,3) = bll.z;
            break;
         case M3D_VOL_FACE_FRONT:
            m3dPolyVertexX(poly,0) = fur.x;   // 1
            m3dPolyVertexY(poly,0) = bll.y;
            m3dPolyVertexZ(poly,0) = bll.z;
            m3dPolyVertexX(poly,1) = fur.x;   // 2
            m3dPolyVertexY(poly,1) = fur.y;
            m3dPolyVertexZ(poly,1) = bll.z;
            *m3dPolyVertex(poly,2) = fur;     // 6
            m3dPolyVertexX(poly,3) = fur.x;   // 5
            m3dPolyVertexY(poly,3) = bll.y;   
            m3dPolyVertexZ(poly,3) = fur.z;   
            break;
         case M3D_VOL_FACE_RIGHT:
            m3dPolyVertexX(poly,0) = bll.x;   // 4
            m3dPolyVertexY(poly,0) = bll.y;
            m3dPolyVertexZ(poly,0) = fur.z;
            m3dPolyVertexX(poly,1) = fur.x;   // 5
            m3dPolyVertexY(poly,1) = bll.y;   
            m3dPolyVertexZ(poly,1) = fur.z;   
            *m3dPolyVertex(poly,2) = fur;     // 6
            m3dPolyVertexX(poly,3) = bll.x;   // 7
            m3dPolyVertexY(poly,3) = fur.y;
            m3dPolyVertexZ(poly,3) = fur.z;
            break;
         case M3D_VOL_FACE_BACK:
            *m3dPolyVertex(poly,0) = bll;     // 0
            m3dPolyVertexX(poly,1) = bll.x;   // 4
            m3dPolyVertexY(poly,1) = bll.y;
            m3dPolyVertexZ(poly,1) = fur.z;
            m3dPolyVertexX(poly,2) = bll.x;   // 7
            m3dPolyVertexY(poly,2) = fur.y;
            m3dPolyVertexZ(poly,2) = fur.z;
            m3dPolyVertexX(poly,3) = bll.x;   // 3
            m3dPolyVertexY(poly,3) = fur.y;
            m3dPolyVertexZ(poly,3) = bll.z;
            break;
         case M3D_VOL_FACE_BOTTOM:
            m3dPolyVertexX(poly,0) = fur.x;   // 1
            m3dPolyVertexY(poly,0) = bll.y;
            m3dPolyVertexZ(poly,0) = bll.z;
            m3dPolyVertexX(poly,1) = fur.x;   // 5
            m3dPolyVertexY(poly,1) = bll.y;   
            m3dPolyVertexZ(poly,1) = fur.z;   
            m3dPolyVertexX(poly,2) = bll.x;   // 4
            m3dPolyVertexY(poly,2) = bll.y;
            m3dPolyVertexZ(poly,2) = fur.z;
            *m3dPolyVertex(poly,3) = bll;     // 0
            break;
         case M3D_VOL_FACE_TOP:
            m3dPolyVertexX(poly,0) = bll.x;   // 3
            m3dPolyVertexY(poly,0) = fur.y;
            m3dPolyVertexZ(poly,0) = bll.z;
            m3dPolyVertexX(poly,1) = bll.x;   // 7
            m3dPolyVertexY(poly,1) = fur.y;
            m3dPolyVertexZ(poly,1) = fur.z;
            *m3dPolyVertex(poly,2) = fur;     // 6
            m3dPolyVertexX(poly,3) = fur.x;   // 2
            m3dPolyVertexY(poly,3) = fur.y;
            m3dPolyVertexZ(poly,3) = bll.z;
            break;
         default:
            ASSERT(FALSE);
            break;
      }
      poly->nVert = 4;
   }
   return;
}
/*************************************************************
* m3dBOX::GetFace()
*
* IN:  faceNmb - number of box's face
* OUT: poly    - box face
*
* Get a face from the box external surface
*************************************************************/
void m3dBOX::GetFaceCenter (int faceNmb, m3dV *center)
{
   ASSERT(faceNmb >= 0 && faceNmb < 6);

   GetCenter(center);
   switch (faceNmb) {
      case M3D_VOL_FACE_LEFT:
         center->z = bll.z;
         break;
      case M3D_VOL_FACE_FRONT:
         center->x = fur.x;
         break;
      case M3D_VOL_FACE_RIGHT:
         center->z = fur.z;
         break;
      case M3D_VOL_FACE_BACK:
         center->x = bll.x;
         break;
      case M3D_VOL_FACE_BOTTOM:
         center->y = bll.y;
         break;
      case M3D_VOL_FACE_TOP:
         center->y = fur.y;
         break;
   }
   return;
}

/*************************************************************
* m3dBOX::GetSurface()
*
* IN:  polyList - six faces of box
*
* Create an array of 6 polygons comprising the outer surface 
* of a box.
*************************************************************/
void m3dBOX::GetSurface (m3dPOLY polyList[6])
{
   int   i;

   for (i = 0; i < 6; i++) {
      GetFace(i, &polyList[i]);
   }
   return;
}


/*******************************************************
* m3dBOX::CalcEnclSph()
*
*******************************************************/
void m3dBOX::CalcEnclSph (m3dSPHERE *pSph)
{
   GetCenter(&pSph->org);
   pSph->radius = m3dDist(&bll, &pSph->org);
   return ;
}

/*******************************************************
* m3dBOX::CheckEnclSph()
*
*******************************************************/
BOOL m3dBOX::CheckEnclSph (m3dSPHERE *pSph)
{
   int         i;
   m3dV        vertList[8];
   m3dSPHERE   sph;

   sph = *pSph;
   sph.radius += 100*M3D_EPSILON;

   GetVertices(vertList);
   for (i = 0; i < 8; i++) {
      if (!sph.IsBelongPoint(&vertList[i])) {
         return FALSE;
      }
   }
   return TRUE;
}

/*************************************************************
* m3dBOX::IsBelongPointFace()
*
* Check if a point belongs to a face.
* It is known that the point lies in the plane of the face.
*************************************************************/
BOOL m3dBOX::IsBelongPointFace (int indFace, m3dV *p)
{
   switch (indFace) {
      case 0:
      case 2:
         if (p->x > bll.x - M3D_EPSILON &&
             p->y > bll.y - M3D_EPSILON &&
             p->x < fur.x + M3D_EPSILON &&
             p->y < fur.y + M3D_EPSILON )  {
            return TRUE;
         }
         break;
      case 1:
      case 3:
         if (p->y > bll.y - M3D_EPSILON &&
             p->z > bll.z - M3D_EPSILON &&
             p->y < fur.y + M3D_EPSILON &&
             p->z < fur.z + M3D_EPSILON )  {
            return TRUE;
         }
         break;
      case 4:
      case 5:
         if (p->x > bll.x - M3D_EPSILON &&
             p->z > bll.z - M3D_EPSILON &&
             p->x < fur.x + M3D_EPSILON &&
             p->z < fur.z + M3D_EPSILON )  {
            return TRUE;
         }
         break;
   }
   return FALSE;
}

/*************************************************************
* m3dBOX::GetCenter()
*
* Get box center
*************************************************************/
void m3dBOX::GetCenter (m3dV * __RESTRICT center) const
{
	m3dLineCombine(&fur, 0.5f, &bll, 0.5f, center);
	return;
}

/***************************************************************
* m3dBOX::Scale()
*
***************************************************************/
void m3dBOX::Scale (float x, float y, float z)
{
   m3dV dimHalf;
   m3dSubtractVector(&fur, &bll, &dimHalf);
   ASSERT(dimHalf.x >= 0.f);
   ASSERT(dimHalf.y >= 0.f);
   ASSERT(dimHalf.z >= 0.f);
   _m3dScaleVector(&dimHalf, 0.5f);

   m3dV center;
   m3dLineCombine(&bll, 0.5f, &fur, 0.5f, &center);

   bll.x = center.x - dimHalf.x*x;
   bll.y = center.y - dimHalf.y*y;
   bll.z = center.z - dimHalf.z*z;

   fur.x = center.x + dimHalf.x*x;
   fur.y = center.y + dimHalf.y*y;
   fur.z = center.z + dimHalf.z*z;
   return;
}

/***************************************************************
* m3dBOX::GetVolume()
*
***************************************************************/
float m3dBOX::GetVolume (void)
{
   m3dV  dim;

   m3dSubtractVector(&fur, &bll, &dim);
   ASSERT(dim.x >= 0.f);
   ASSERT(dim.y >= 0.f);
   ASSERT(dim.z >= 0.f);

   return m3dFAbs(dim.x*dim.y*dim.z);
}

/***************************************************************
* m3dBOX::GetArea()
*
***************************************************************/
float m3dBOX::GetArea (void)
{
   m3dV  dim;

   m3dSubtractVector(&fur, &bll, &dim);
   ASSERT(dim.x >= 0.f);
   ASSERT(dim.y >= 0.f);
   ASSERT(dim.z >= 0.f);

   return 2.f*(dim.x*dim.z + dim.x*dim.y + dim.y*dim.z);
}

/*************************************************************
* m3dBOX::IsIsectOBB ()
* 
*************************************************************/
BOOL m3dBOX::IsIsectOBB (m3dOBB *pOBB)
{  
   m3dBOX box;

   pOBB->CalcEnclBox(&box);
   if (!IsIsectBox(&box)) {
      return FALSE;
   }
   m3dOBB obb;
   ConvToOBB(&obb);
   return obb.IsIsectOBB(pOBB);
}


/*************************************************************
* m3dBOX::IsIsectPoly()
*
* Check intersecton of 3D poly with box
* IN:   pPoly - 3D poly
*       pNorm - polygon's normal
* RET:  M3D_ISECT, M3D_INSIDE, M3D_OUTSIDE (isect retcode)
*************************************************************/
int m3dBOX::IsIsectPoly (m3dPOLY *poly, m3dV *norm)
{
   m3dV     normSwap, normTmp, vTmp, vIsect;
   m3dPOLY  polySwap, polyBox;
   m3dBOX   bbSwap;
   m2dPOLY  poly2D, polyRect, polyClipped;
   BOOL     fInside;

   int      rc;
   int      bestPlaneInd; // 0 for YZ, 1 for XZ, 2 for XY
   int      i;
   int      n, n_in, n_up, n_down;

   if (norm == NULL) {
      m3dGetPolyNormal(poly, &normTmp);
      norm = &normTmp;
   } else {
      ASSERT(m3dIsZero(m3dLengthVector(norm) - 1.f));
   }

   if (m3dFAbs(norm->x) >= m3dFAbs(norm->y) && m3dFAbs(norm->x) >= m3dFAbs(norm->z)) {
      bestPlaneInd = 0;
   } else if (m3dFAbs(norm->y) >= m3dFAbs(norm->x) && m3dFAbs(norm->y) >= m3dFAbs(norm->z)) {
      bestPlaneInd = 1;
   } else if (m3dFAbs(norm->z) >= m3dFAbs(norm->x) && m3dFAbs(norm->z) >= m3dFAbs(norm->y)) {
      bestPlaneInd = 2;
   } else {
      ASSERT(FALSE);
      return M3D_ISECT;
   }

   switch (bestPlaneInd) {
      case 0:
         bbSwap.bll.x = bll.z;
         bbSwap.bll.y = bll.x;
         bbSwap.bll.z = bll.y;
         bbSwap.fur.x = fur.z;
         bbSwap.fur.y = fur.x;
         bbSwap.fur.z = fur.y;
         m3dPolyNVert(&polySwap) = m3dPolyNVert(poly);
         for (i = 0; i < polySwap.nVert; i++) {
            polySwap[i].x = (*poly)[i].z;
            polySwap[i].y = (*poly)[i].x;
            polySwap[i].z = (*poly)[i].y;
         }
         normSwap.x = norm->z;
         normSwap.y = norm->x;
         normSwap.z = norm->y;
         break;
      case 1:
         bbSwap = *this;
         polySwap = *poly;
         normSwap = *norm;
         break;
      case 2:
         bbSwap.bll.x = bll.y;
         bbSwap.bll.y = bll.z;
         bbSwap.bll.z = bll.x;
         bbSwap.fur.x = fur.y;
         bbSwap.fur.y = fur.z;
         bbSwap.fur.z = fur.x;
         m3dPolyNVert(&polySwap) = m3dPolyNVert(poly);
         for (i = 0; i < m3dPolyNVert(&polySwap); i++) {
            polySwap[i].x = (*poly)[i].y;
            polySwap[i].y = (*poly)[i].z;
            polySwap[i].z = (*poly)[i].x;
         }
         normSwap.x = norm->y;
         normSwap.y = norm->z;
         normSwap.z = norm->x;
         break;
   }
   bbSwap.GetFace(4, &polyBox);
   m3xzPoly3DToPoly2D(&polyBox, &polyRect);
   m3xzPoly3DToPoly2D(&polySwap, &poly2D);
   rc = m2dClipPolyRect(&poly2D, &polyRect, &polyClipped);
   if (rc == M3D_OUTSIDE) {
      return M3D_OUTSIDE;
   }
   if (rc == M3D_INSIDE) {
      fInside = TRUE;
   } else {
      fInside = FALSE;
   }

   n_in   = 0;
   n_up   = 0;
   n_down = 0;
   for (n = 0; n < m3dPolyNVert(&polyClipped); n++) {
      m3xzV2DToV3D(m3dPolyVertex(&polyClipped,n), &vTmp);
      rc = m3dIsIsectYLinePlane(&vTmp, m3dPolyVertex(&polySwap,0), &normSwap, &vIsect);
      ASSERT(rc);
      if (vIsect.y >= bbSwap.bll.y && vIsect.y <= bbSwap.fur.y) {
         n_in++;
      }
      if (vIsect.y > bbSwap.fur.y) {
         n_up++;
      }
      if (vIsect.y < bbSwap.bll.y) {
         n_down++;
      }
   }
   if (n_down == 0 && n_in == 0) {
      return M3D_OUTSIDE;
   }
   if (n_up == 0 && n_in == 0) {
      return M3D_OUTSIDE;
   }
   if (n_up == 0 && n_down == 0 && fInside) {
      return M3D_INSIDE;
   }
   return M3D_ISECT;
}

/*************************************************************
* m3dBOX::IsIsectPolyXZ()
*
* Check intersecton of 3D-XZ poly with box
* IN:   poly - 3D poly
*       norm - polygon's normal
* RET:  M3D_ISECT, M3D_INSIDE, M3D_OUTSIDE (isect retcode)
*************************************************************/
int m3dBOX::IsIsectPolyXZ (m3dPOLY *poly, m3dV *norm)
{
   m2dPOLY polyIn, polyOut, polyRect;
   m3dPOLY polyBox;
   m2dV    lB, lE, lBClp, lEClp;
   int     rc;

   // Clip by XZ plane
   m3xzPoly3DToPoly2D(poly, &polyIn);
   GetFace(M3D_VOL_FACE_BOTTOM, &polyBox);
   m3xzPoly3DToPoly2D(&polyBox, &polyRect);
   if (m3dFAbs(m3dDotProduct(norm, &m3dVUnitY)) > 10.f*M3D_EPSILON) {
      rc = m2dClipPolyRect(&polyIn, &polyRect, &polyOut);
   } else {
      // projected polygon is degenerate (line segment)
      m2dGetPolyDiameter(&polyIn, &lB, &lE);
      rc = m2dClipLSegRect(&lB, &lE, &polyRect, &lBClp, &lEClp);
   }
   return rc;
}

/*************************************************************
* m3dBOX::ClipLSeg2 ()
*
* RET:  M3D_ISECT, M3D_INSIDE, M3D_OUTSIDE (isect retcode)
* NOTE: v1 and v2 are valid only if ret code is not M3D_OUTSIDE
*************************************************************/
int m3dBOX::ClipLSeg2 (m3dV *_vFrom, m3dV *_vTo, m3dV &vFrom, m3dV &vTo)
{
   // setup default
   int rc = M3D_INSIDE;
   vFrom = *_vFrom; 
   vTo   = *_vTo;   

   // change default to real
   float dist;
   if (IsBelongPoint(_vFrom)) {
      if (IsBelongPoint(_vTo)) {
         rc = M3D_INSIDE;
      } else {
         // first point belong to box but second not
         m3dV dir;
         m3dSubtractVector(_vFrom, _vTo, &dir);
         float lenRay = m3dDist(_vTo, _vFrom);
         if (lenRay > M3D_EPSILON) {
            m3dNormalize(&dir);
            if (IsectRay(_vTo, &dir, lenRay, &vTo, &dist) == M3D_ISECT_YES) {
               rc = M3D_ISECT;
            } else {
               ASSERT(FALSE);
               rc = M3D_INSIDE;
               vTo = *_vTo;
            }
         }
      }
   } else if (IsBelongPoint(_vTo)) {
      // second point belong to box but first not
      m3dV dir;
      m3dSubtractVector(_vTo, _vFrom, &dir);
      float lenRay = m3dDist(_vTo, _vFrom);
      if (lenRay > M3D_EPSILON) {
         m3dNormalize(&dir);
         if (IsectRay(_vFrom, &dir, lenRay, &vFrom, &dist) == M3D_ISECT_YES) {
            rc = M3D_ISECT;
         } else {
            ASSERT(FALSE);
            rc = M3D_INSIDE;
            vFrom = *_vFrom;
         }
      }
   } else {
      // get first intersection: if any exist
      {
         m3dV dir;
         m3dSubtractVector(_vTo, _vFrom, &dir);
         float lenRay = m3dDist(_vTo, _vFrom);
         if (lenRay > M3D_EPSILON) {
            m3dNormalize(&dir);
            rc = IsectRay(_vFrom, &dir, lenRay, &vFrom, &dist);
            if (rc == M3D_ISECT_NO) {
               vFrom = vTo = m3dVZero;
               return M3D_OUTSIDE;
            } else {
               ASSERT(rc == M3D_ISECT_YES);
               rc = M3D_ISECT;
            }
         }
      }
      // get second intersection
      {
         m3dV dir;
         m3dSubtractVector(_vFrom, _vTo, &dir);
         float lenRay = m3dDist(_vTo, _vFrom);
         if (lenRay > M3D_EPSILON) {
            m3dNormalize(&dir);
            rc = IsectRay(_vTo, &dir, lenRay, &vTo, &dist);
            if (rc == M3D_ISECT_NO) {
               vFrom = vTo = m3dVZero;
               return M3D_OUTSIDE;
            } else {
               ASSERT(rc == M3D_ISECT_YES);
               rc = M3D_ISECT;
            }
         }
      }
   }
   return rc;
}

/*************************************************************
* m3dBOX::QualifyBoxINTEGER ()
*
* RET:  TRUE if isect, FALSE outside
*************************************************************/
m3dBOX::QUAL_INT m3dBOX::QualifyBoxINTEGER (const m3dBOX &box1, const m3dBOX &box2)
{
   // check outside
   if ((int)box1.bll.x > (int)box2.fur.x || 
       (int)box1.bll.z > (int)box2.fur.z || 
       (int)box1.bll.y > (int)box2.fur.y ||
       // 2
       (int)box1.fur.x < (int)box2.bll.x || 
       (int)box1.fur.z < (int)box2.bll.z || 
       (int)box1.fur.y < (int)box2.bll.y) {
      return QUAL_INT_OUTSIDE;
   }

   return QUAL_INT_ISECT;
}

/*************************************************************
* m3dBOX::ClipBox ()
*
* RET:  M3D_ISECT, M3D_INSIDE, M3D_OUTSIDE (isect retcode)
* NOTE: boxClip is valid only if ret code is not M3D_OUTSIDE
*************************************************************/
int m3dBOX::ClipBox (const m3dBOX &box, m3dBOX *pBoxClip) const
{
   // check outside
   if (bll.x > box.fur.x || bll.z > box.fur.z || bll.y > box.fur.y ||
       fur.x < box.bll.x || fur.z < box.bll.z || fur.y < box.bll.y) {
#ifdef _DEBUG
      if (pBoxClip) {
         pBoxClip->bll = pBoxClip->fur = m3dVZero;
      }
#endif
      return M3D_OUTSIDE;
   }

   // isect or inside
   int rc = M3D_INSIDE;
   if (bll.x > box.bll.x || bll.y > box.bll.y || bll.z > box.bll.z ||
      fur.x < box.fur.x || fur.y < box.fur.y || fur.z < box.fur.z) {
      rc = M3D_ISECT;
   }
   if (!pBoxClip) {
      return rc;
   }

   // check inside
   *pBoxClip = box;
   if (bll.x > box.bll.x) {
      pBoxClip->bll.x = bll.x;
   }
   if (bll.y > box.bll.y) {
      pBoxClip->bll.y = bll.y;
   }
   if (bll.z > box.bll.z) {
      pBoxClip->bll.z = bll.z;
   }
   if (fur.x < box.fur.x) {
      pBoxClip->fur.x = fur.x;
   }
   if (fur.y < box.fur.y) {
      pBoxClip->fur.y = fur.y;
   }
   if (fur.z < box.fur.z) {
      pBoxClip->fur.z = fur.z;
   }
#ifdef _DEBUG
   if (!pBoxClip->Check()) {
      ASSERT(FALSE);
   }
#endif
   return rc;
}

/*************************************************************
* m3dBOX::ClipLSeg()
*
* Clip a lseg by box
* NOTE: If point belongs to box, the intersection data (v, dist)
*       is not computed
*
* IN:  vFrom/vTo     - line segment
* OUT: v1,v2         - point if intersection (if exists)
* RET: TRUE if isection exist
*************************************************************/
BOOL m3dBOX::ClipLSeg (m3dV *vFrom, m3dV *vTo, m3dV *v1, m3dV *v2)
{
   float x1, y1, x2, y2, z1, z2;
   int   code1, code2;
#ifdef _DEBUG
   int   nRepeat = 0;
#endif

   x1 = this->bll.x;
   y1 = this->bll.y;
   z1 = this->bll.z;
   x2 = this->fur.x;
   y2 = this->fur.y;
   z2 = this->fur.z;
   m3dCmpSwap(&x1, &x2);
   m3dCmpSwap(&y1, &y2);
   m3dCmpSwap(&z1, &z2);
   *v1 = *vFrom;
   *v2 = *vTo;

   code1 = _m3dGetPointCodeBox(v1, x1, y1, z1, x2, y2, z2);
   code2 = _m3dGetPointCodeBox(v2, x1, y1, z1, x2, y2, z2);

   int counter = 0;
   while (code1 | code2) {
      counter++;
      if (counter > 200) {
         ASSERT(FALSE);
      }
      if (code1 & code2) {
         return FALSE;
      }
#ifdef _DEBUG
      if (nRepeat++ > 100) {
         ASSERT(FALSE);
         break;
      }
#endif

      if (code1 & M3D_CODE_LEFT) { // x<x1
         v1->z = v1->z + (v2->z - v1->z)*(x1 - v1->x)/(v2->x - v1->x);
         v1->y = v1->y + (v2->y - v1->y)*(x1 - v1->x)/(v2->x - v1->x);
         v1->x = x1;
      } else if (code1 & M3D_CODE_ABOVE) { // y<y1
         v1->z = v1->z + (v2->z - v1->z)*(y1 - v1->y)/(v2->y - v1->y);
         v1->x = v1->x + (v2->x - v1->x)*(y1 - v1->y)/(v2->y - v1->y);
         v1->y = y1;
      } else if (code1 & M3D_CODE_RIGHT) { // z<z1
         v1->x = v1->x + (v2->x - v1->x)*(z1 - v1->z)/(v2->z - v1->z);
         v1->y = v1->y + (v2->y - v1->y)*(z1 - v1->z)/(v2->z - v1->z);
         v1->z = z1;
      } else if (code1 & M3D_CODE_BELOW) { // x>x2
         v1->z = v1->z + (v2->z - v1->z)*(x2 - v1->x)/(v2->x - v1->x);
         v1->y = v1->y + (v2->y - v1->y)*(x2 - v1->x)/(v2->x - v1->x);
         v1->x = x2;
      } else if (code1 & M3D_CODE_ABOVE_Z) { // y>y2
         v1->z = v1->z + (v2->z - v1->z)*(y2 - v1->y)/(v2->y - v1->y);
         v1->x = v1->x + (v2->x - v1->x)*(y2 - v1->y)/(v2->y - v1->y);
         v1->y = y2;
      } else if (code1 & M3D_CODE_BELOW_Z) { // z>z2
         v1->x = v1->x + (v2->x - v1->x)*(z2 - v1->z)/(v2->z - v1->z);
         v1->y = v1->y + (v2->y - v1->y)*(z2 - v1->z)/(v2->z - v1->z);
         v1->z = z2;
      }
      code1 = _m3dGetPointCodeBox(v1, x1, y1, z1, x2, y2, z2);
      if (code1 & code2) {
         return FALSE;
      }

      if (code2 & M3D_CODE_LEFT) { // x<x1
         v2->z = v1->z + (v2->z - v1->z)*(x1 - v1->x)/(v2->x - v1->x);
         v2->y = v1->y + (v2->y - v1->y)*(x1 - v1->x)/(v2->x - v1->x);
         v2->x = x1;
      } else if (code2 & M3D_CODE_ABOVE) { // y<y1
         v2->z = v1->z + (v2->z - v1->z)*(y1 - v1->y)/(v2->y - v1->y);
         v2->x = v1->x + (v2->x - v1->x)*(y1 - v1->y)/(v2->y - v1->y);
         v2->y = y1;
      } else if (code2 & M3D_CODE_RIGHT) { // z<z1
         v2->x = v1->x + (v2->x - v1->x)*(z1 - v1->z)/(v2->z - v1->z);
         v2->y = v1->y + (v2->y - v1->y)*(z1 - v1->z)/(v2->z - v1->z);
         v2->z = z1;
      } else if (code2 & M3D_CODE_BELOW) { // x>x2
         v2->z = v1->z + (v2->z - v1->z)*(x2 - v1->x)/(v2->x - v1->x);
         v2->y = v1->y + (v2->y - v1->y)*(x2 - v1->x)/(v2->x - v1->x);
         v2->x = x2;
      } else if (code2 & M3D_CODE_ABOVE_Z) { // y>y2
         v2->z = v1->z + (v2->z - v1->z)*(y2 - v1->y)/(v2->y - v1->y);
         v2->x = v1->x + (v2->x - v1->x)*(y2 - v1->y)/(v2->y - v1->y);
         v2->y = y2;
      } else if (code2 & M3D_CODE_BELOW_Z) { // z>z2
         v2->x = v1->x + (v2->x - v1->x)*(z2 - v1->z)/(v2->z - v1->z);
         v2->y = v1->y + (v2->y - v1->y)*(z2 - v1->z)/(v2->z - v1->z);
         v2->z = z2;
      }
      code2 = _m3dGetPointCodeBox(v2, x1, y1, z1, x2, y2, z2);
   }
   return TRUE;
}

/************************************************************
* m3dBOX::QualPlane ()
*
************************************************************/
int m3dBOX::QualPlane (m3dV *planeOrg, m3dV *planeNorm)
{
   m3dV   vList[8];

   GetVertices(vList);
   return m3dQualVListPlane(planeOrg, planeNorm, 8, vList);
}
/************************************************************
* m3dBOX::Transform ()
*
************************************************************/
void m3dBOX::Transform (m3dMATR *pLCS2WCS, class m3dBOX *bb) const
{
   m3dV   vList[8];

   GetVertices(vList);
   pLCS2WCS->TransformPointList(8, vList, sizeof(m3dV), vList, sizeof(m3dV));
   bb->Calc(vList, 8);
   return;
}
/************************************************************
* m3dBOX::Transform ()
*
************************************************************/
void m3dBOX::Transform (m3dMATR *pLCS2WCS, class m3dOBB *obb) const
{
   float sx, sy, sz;

   pLCS2WCS->TransformPoint(&bll, obb->GetOrg());

   *obb->GetDirX() = *pLCS2WCS->GetAxisX();
   *obb->GetDirY() = *pLCS2WCS->GetAxisY();
   *obb->GetDirZ() = *pLCS2WCS->GetAxisZ();
   sx = m3dLengthVector(obb->GetDirX());
   sy = m3dLengthVector(obb->GetDirY());
   sz = m3dLengthVector(obb->GetDirZ());
   _m3dScaleVector(obb->GetDirX(), 1.f/sx);
   _m3dScaleVector(obb->GetDirY(), 1.f/sy);
   _m3dScaleVector(obb->GetDirZ(), 1.f/sz);
   obb->GetSizeX() = sx * (fur.x-bll.x);
   obb->GetSizeY() = sy * (fur.y-bll.y);
   obb->GetSizeZ() = sz * (fur.z-bll.z);
   return;
}

/************************************************************
* m3dBOX::Transform2OBB_ADV ()
*
************************************************************/
void m3dBOX::Transform2OBB_ADV (m3dMATR *pLCS2WCS, class m3dOBB *obb) const
{
   float sx, sy, sz;

   pLCS2WCS->TransformPoint(&bll, obb->GetOrg());

   *obb->GetDirX() = *pLCS2WCS->GetAxisX();
   *obb->GetDirY() = *pLCS2WCS->GetAxisY();
   *obb->GetDirZ() = *pLCS2WCS->GetAxisZ();
   sx = m3dLengthVector(obb->GetDirX());
   sy = m3dLengthVector(obb->GetDirY());
   sz = m3dLengthVector(obb->GetDirZ());
   BOOL isOrto = TRUE;
   if (!m3dIsZero_A(m3dDotProduct(obb->GetDirX(),obb->GetDirY()),0.02f) ||
       !m3dIsZero_A(m3dDotProduct(obb->GetDirX(),obb->GetDirZ()),0.02f) ||
       !m3dIsZero_A(m3dDotProduct(obb->GetDirY(),obb->GetDirZ()),0.02f)) {
      isOrto = FALSE;
   }
   BOOL isScaleZero = FALSE;
   if (m3dFAbs(sx) < M3D_EPSILON || m3dFAbs(sy) < M3D_EPSILON || m3dFAbs(sz) < M3D_EPSILON) {
      isScaleZero = TRUE;
   }
   if (isOrto && !isScaleZero) {
      _m3dScaleVector(obb->GetDirX(), 1.f/sx);
      _m3dScaleVector(obb->GetDirY(), 1.f/sy);
      _m3dScaleVector(obb->GetDirZ(), 1.f/sz);
      obb->GetSizeX() = sx * (fur.x-bll.x);
      obb->GetSizeY() = sy * (fur.y-bll.y);
      obb->GetSizeZ() = sz * (fur.z-bll.z);
   } else {
      //ASSERT(FALSE);
      *obb->GetDirX() = m3dVUnitX;
      *obb->GetDirY() = m3dVUnitY;
      *obb->GetDirZ() = m3dVUnitZ;
      float maxScale = max(max(sx,sy),sz);
      obb->GetSizeX() = maxScale * (fur.x-bll.x);
      obb->GetSizeY() = maxScale * (fur.y-bll.y);
      obb->GetSizeZ() = maxScale * (fur.z-bll.z);
   }
   return;
}

/************************************************************
* m3dBOX::ConvToOBB ()
*
************************************************************/
void m3dBOX::ConvToOBB (m3dOBB *obb) const
{
   *obb->GetOrg()  = bll;
   *obb->GetDirX() = m3dVUnitX;
   *obb->GetDirY() = m3dVUnitY;
   *obb->GetDirZ() = m3dVUnitZ;
   obb->GetSizeX() = fur.x-bll.x;
   obb->GetSizeY() = fur.y-bll.y;
   obb->GetSizeZ() = fur.z-bll.z;
   return;
}
/*************************************************************
* _m3dGetPointCodeBox()
*
*************************************************************/
static int _m3dGetPointCodeBox (m3dV *point, float x1, float y1, float z1, float x2, float y2, float z2)
{
   int code = 0;

   if (point->x < x1) code |= M3D_CODE_LEFT;
   if (point->y < y1) code |= M3D_CODE_ABOVE;
   if (point->z < z1) code |= M3D_CODE_RIGHT;
   if (point->x > x2) code |= M3D_CODE_BELOW;
   if (point->y > y2) code |= M3D_CODE_ABOVE_Z;
   if (point->z > z2) code |= M3D_CODE_BELOW_Z;
   return code;
}
//
// End-of-file M3D_BOX.CPP
//
